#ifndef UTILS_H
#define UTILS_H

#include <QObject>
#include <QMetaType>
#include <QMetaEnum>

#if defined(Q_OS_LINUX) || defined(Q_CC_MINGW)
#include <signal.h>
#include <unistd.h>
#endif

#ifndef RegisterMetaTypes
#define RegisterMetaTypes(X) { \
qRegisterMetaType<X>(#X); \
    qRegisterMetaType<X>(#X"&"); \
    qRegisterMetaType< QVector<X> >("QVector<"#X">"); \
    qRegisterMetaType< QVector<X> >("QVector<"#X">&"); \
}
#endif

#ifndef RegisterMetaTypeStreamOperators
#define RegisterMetaTypeStreamOperators(X) { \
qRegisterMetaTypeStreamOperators<X>(#X); \
    qRegisterMetaTypeStreamOperators<X>(#X"&"); \
    qRegisterMetaTypeStreamOperators< QVector<X> >("QVector<"#X">"); \
    qRegisterMetaTypeStreamOperators< QVector<X> >("QVector<"#X">&"); \
}
#endif

#ifndef SAFE_DELETE
#define SAFE_DELETE(x)  if (x) { delete x; x = nullptr; }
#endif

#ifndef FUNCTION_TRACE
#if defined (DEBUG_TRACE)
#define FUNCTION_TRACE     qDebug().noquote() << Q_FUNC_INFO
#else
#define FUNCTION_TRACE
#endif
#endif

typedef void (*SignalHandlerCallback)(int);

class Utils : public QObject
{
    Q_OBJECT
public:
    static Utils &GetInstance() {
        static Utils utils;
        return utils;
    }

    void ExitAfter(qint32 msec);
    void HandelSignal(qint32 signal_number, SignalHandlerCallback callback);

    static void m_sleep(quint32 ms);

    static bool AttachOutputToConsole(void);

    /* Enum Convertor */
    /* NOTE: When you want to use this convertor, your enum must have a menber hold the -1 */
    template <typename T, typename std::enable_if_t<std::is_enum<T>::value, bool> = true>
    static T EnumConvert(const char* t) {
        Q_STATIC_ASSERT_X(QtPrivate::IsQEnumHelper<T>::Value, "Type is not a enumerator or not declared with Q_ENUM, Q_ENUM_NS, Q_FLAG or Q_FLAG_NS");
        QMetaEnum convertor = QMetaEnum::fromType<T>();
        return static_cast<T>(convertor.keyToValue(t));
    }

    template <typename T, typename std::enable_if_t<std::is_enum<T>::value, bool> = true>
    static T EnumConvert(const QString &t) {
        Q_STATIC_ASSERT_X(QtPrivate::IsQEnumHelper<T>::Value, "Type is not a enumerator or not declared with Q_ENUM, Q_ENUM_NS, Q_FLAG or Q_FLAG_NS");
        QMetaEnum convertor = QMetaEnum::fromType<T>();
        return static_cast<T>(convertor.keyToValue(t.toUtf8()));
    }

    template <typename T, typename std::enable_if_t<std::is_enum<T>::value, bool> = true>
    static QByteArray EnumConvert(T t) {
        Q_STATIC_ASSERT_X(QtPrivate::IsQEnumHelper<T>::Value, "Type is not a enumerator or not declared with Q_ENUM, Q_ENUM_NS, Q_FLAG or Q_FLAG_NS");
        QMetaEnum convertor = QMetaEnum::fromType<T>();
        return convertor.valueToKey(static_cast<int>(t));
    }

private:
    explicit Utils(QObject *parent = nullptr);
    Utils(const Utils &) = delete;
    Utils(const Utils &&) = delete;
    Utils &operator=(const Utils &) = delete;
};

#endif // UTILS_H
